package com.yy.yunpan.file.service.impl;

import com.yy.yunpan.common.constant.Constants;
import com.yy.yunpan.common.exception.BusinessException;
import com.yy.yunpan.common.result.ResultCode;
import com.yy.yunpan.common.util.OssUtil;
import com.yy.yunpan.file.entity.File;
import com.yy.yunpan.file.repository.FileRepository;
import com.yy.yunpan.file.service.FileService;
import com.yy.yunpan.user.entity.User;
import com.yy.yunpan.user.repository.UserRepository;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 文件服务实现类
 *
 * @author YY
 * @since 2025-10-01
 */
@Service
@Transactional
public class FileServiceImpl implements FileService {

    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    @Autowired
    private FileRepository fileRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OssUtil ossUtil;

    @Override
    public Map<String, Object> getUploadSignature(Long userId, String fileName, Long fileSize, String contentType, Long parentId) {
        // 参数校验
        if (userId == null || StringUtils.isBlank(fileName) || fileSize == null || fileSize <= 0) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 检查文件大小限制
        if (fileSize > Constants.MAX_FILE_SIZE) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件大小超过限制");
        }

        // 检查用户存储空间
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_EXIST));

        Long usedStorage = fileRepository.sumFileSizeByUserId(userId);
        if (usedStorage + fileSize > user.getStorageQuota()) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "存储空间不足");
        }

        // 检查同名文件
        Optional<File> existingFile = fileRepository.findByUserIdAndFileNameAndParentIdAndStatus(
                userId, fileName, parentId != null ? parentId : 0L, Constants.FileStatus.NORMAL);
        if (existingFile.isPresent()) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件名已存在");
        }

        // 生成OSS上传签名
        try {
            Map<String, Object> signature = ossUtil.generatePostSignature(userId, fileName, fileSize, contentType);
            logger.info("生成上传签名成功：用户ID={}, 文件名={}", userId, fileName);
            return signature;
        } catch (Exception e) {
            logger.error("生成上传签名失败：用户ID={}, 文件名={}", userId, fileName, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "生成上传签名失败");
        }
    }

    @Override
    public File saveFileMetadata(Long userId, String fileName, Long fileSize, String fileType, String ossKey, Long parentId, String md5) {
        // 参数校验
        if (userId == null || StringUtils.isBlank(fileName) || fileSize == null || StringUtils.isBlank(ossKey)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 检查文件是否已存在（防止重复上传）
        Optional<File> existingFile = fileRepository.findByUserIdAndFileNameAndParentIdAndStatus(
                userId, fileName, parentId != null ? parentId : 0L, Constants.FileStatus.NORMAL);
        if (existingFile.isPresent()) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件已存在");
        }

        // 创建文件记录
        File file = new File();
        file.setUserId(userId);
        file.setFileName(fileName);
        file.setFileSize(fileSize);
        file.setFileType(fileType);
        file.setOssKey(ossKey);
        file.setParentId(parentId != null ? parentId : 0L);
        file.setMd5(md5);
        file.setIsFolder(0);
        file.setStatus(Constants.FileStatus.NORMAL);

        try {
            file = fileRepository.save(file);

            // 更新用户已使用存储空间
            Long usedStorage = fileRepository.sumFileSizeByUserId(userId);
            User user = userRepository.findById(userId).orElseThrow();
            user.setUsedStorage(usedStorage);
            userRepository.save(user);

            logger.info("保存文件元数据成功：用户ID={}, 文件名={}, 文件ID={}", userId, fileName, file.getId());
            return file;
        } catch (Exception e) {
            logger.error("保存文件元数据失败：用户ID={}, 文件名={}", userId, fileName, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "保存文件失败");
        }
    }

    @Override
    public Page<File> getFileList(Long userId, Long parentId, Integer page, Integer size, String orderBy, String orderType) {
        // 参数校验和默认值设置
        if (userId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }
        if (parentId == null) parentId = 0L;
        if (page == null || page < 1) page = 1;
        if (size == null || size < 1) size = 20;
        if (StringUtils.isBlank(orderBy)) orderBy = "createTime";
        if (StringUtils.isBlank(orderType)) orderType = "desc";

        // 创建排序对象
        Sort.Direction direction = "asc".equalsIgnoreCase(orderType) ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, orderBy);

        // 创建分页对象
        Pageable pageable = PageRequest.of(page - 1, size, sort);

        try {
            Page<File> filePage = fileRepository.findByUserIdAndParentIdAndStatus(userId, parentId, Constants.FileStatus.NORMAL, pageable);
            logger.info("获取文件列表成功：用户ID={}, 父目录ID={}, 总数={}", userId, parentId, filePage.getTotalElements());
            return filePage;
        } catch (Exception e) {
            logger.error("获取文件列表失败：用户ID={}, 父目录ID={}", userId, parentId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "获取文件列表失败");
        }
    }

    @Override
    public File createFolder(Long userId, String folderName, Long parentId) {
        // 参数校验
        if (userId == null || StringUtils.isBlank(folderName)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }
        if (parentId == null) parentId = 0L;

        // 检查同名文件夹
        Optional<File> existingFolder = fileRepository.findByUserIdAndFileNameAndParentIdAndStatus(
                userId, folderName, parentId, Constants.FileStatus.NORMAL);
        if (existingFolder.isPresent()) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件夹名已存在");
        }

        // 创建文件夹
        File folder = new File();
        folder.setUserId(userId);
        folder.setFileName(folderName);
        folder.setParentId(parentId);
        folder.setIsFolder(1);
        folder.setStatus(Constants.FileStatus.NORMAL);

        try {
            folder = fileRepository.save(folder);
            logger.info("创建文件夹成功：用户ID={}, 文件夹名={}, 文件夹ID={}", userId, folderName, folder.getId());
            return folder;
        } catch (Exception e) {
            logger.error("创建文件夹失败：用户ID={}, 文件夹名={}", userId, folderName, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "创建文件夹失败");
        }
    }

    @Override
    public void deleteFile(Long userId, Long fileId) {
        // 参数校验
        if (userId == null || fileId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        try {
            // 移动到回收站
            file.setStatus(Constants.FileStatus.RECYCLE);
            fileRepository.save(file);

            logger.info("删除文件成功：用户ID={}, 文件ID={}, 文件名={}", userId, fileId, file.getFileName());
        } catch (Exception e) {
            logger.error("删除文件失败：用户ID={}, 文件ID={}", userId, fileId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "删除文件失败");
        }
    }

    @Override
    public File renameFile(Long userId, Long fileId, String newName) {
        // 参数校验
        if (userId == null || fileId == null || StringUtils.isBlank(newName)) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        // 检查同名文件
        Optional<File> existingFile = fileRepository.findByUserIdAndFileNameAndParentIdAndStatus(
                userId, newName, file.getParentId(), Constants.FileStatus.NORMAL);
        if (existingFile.isPresent() && !existingFile.get().getId().equals(fileId)) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件名已存在");
        }

        try {
            file.setFileName(newName);
            file = fileRepository.save(file);

            logger.info("重命名文件成功：用户ID={}, 文件ID={}, 新名称={}", userId, fileId, newName);
            return file;
        } catch (Exception e) {
            logger.error("重命名文件失败：用户ID={}, 文件ID={}, 新名称={}", userId, fileId, newName, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "重命名文件失败");
        }
    }

    @Override
    public File moveFile(Long userId, Long fileId, Long targetParentId) {
        // 参数校验
        if (userId == null || fileId == null || targetParentId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        // 检查目标目录下是否有同名文件
        Optional<File> existingFile = fileRepository.findByUserIdAndFileNameAndParentIdAndStatus(
                userId, file.getFileName(), targetParentId, Constants.FileStatus.NORMAL);
        if (existingFile.isPresent()) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "目标目录下已存在同名文件");
        }

        try {
            file.setParentId(targetParentId);
            file = fileRepository.save(file);

            logger.info("移动文件成功：用户ID={}, 文件ID={}, 目标父目录ID={}", userId, fileId, targetParentId);
            return file;
        } catch (Exception e) {
            logger.error("移动文件失败：用户ID={}, 文件ID={}, 目标父目录ID={}", userId, fileId, targetParentId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "移动文件失败");
        }
    }

    @Override
    public Map<String, Object> getStorageInfo(Long userId) {
        // 参数校验
        if (userId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        try {
            User user = userRepository.findById(userId)
                    .orElseThrow(() -> new BusinessException(ResultCode.USER_NOT_EXIST));

            Long usedStorage = fileRepository.sumFileSizeByUserId(userId);

            Map<String, Object> storageInfo = new HashMap<>();
            storageInfo.put("storageQuota", user.getStorageQuota());
            storageInfo.put("usedStorage", usedStorage);
            storageInfo.put("availableStorage", user.getStorageQuota() - usedStorage);
            storageInfo.put("usagePercentage", user.getStorageQuota() > 0 ? (usedStorage * 100.0 / user.getStorageQuota()) : 0);

            return storageInfo;
        } catch (Exception e) {
            logger.error("获取存储信息失败：用户ID={}", userId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "获取存储信息失败");
        }
    }

    @Override
    public Page<File> getRecycleFileList(Long userId, Integer page, Integer size, String orderBy, String orderType) {
        // 参数校验和默认值设置
        if (userId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }
        if (page == null || page < 1) page = 1;
        if (size == null || size < 1) size = 20;
        if (StringUtils.isBlank(orderBy)) orderBy = "updateTime";
        if (StringUtils.isBlank(orderType)) orderType = "desc";

        // 创建排序对象
        Sort.Direction direction = "asc".equalsIgnoreCase(orderType) ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, orderBy);

        // 创建分页对象
        Pageable pageable = PageRequest.of(page - 1, size, sort);

        try {
            Page<File> filePage = fileRepository.findByUserIdAndStatus(userId, Constants.FileStatus.RECYCLE, pageable);
            logger.info("获取回收站文件列表成功：用户ID={}, 总数={}", userId, filePage.getTotalElements());
            return filePage;
        } catch (Exception e) {
            logger.error("获取回收站文件列表失败：用户ID={}", userId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "获取回收站文件列表失败");
        }
    }

    @Override
    public File restoreFile(Long userId, Long fileId) {
        // 参数校验
        if (userId == null || fileId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找回收站中的文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        if (!Integer.valueOf(Constants.FileStatus.RECYCLE).equals(file.getStatus())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件不在回收站中");
        }

        try {
            // 恢复文件状态
            file.setStatus(Constants.FileStatus.NORMAL);
            file = fileRepository.save(file);

            logger.info("恢复文件成功：用户ID={}, 文件ID={}, 文件名={}", userId, fileId, file.getFileName());
            return file;
        } catch (Exception e) {
            logger.error("恢复文件失败：用户ID={}, 文件ID={}", userId, fileId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "恢复文件失败");
        }
    }

    @Override
    public void permanentDeleteFile(Long userId, Long fileId) {
        // 参数校验
        if (userId == null || fileId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找回收站中的文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        if (!Integer.valueOf(Constants.FileStatus.RECYCLE).equals(file.getStatus())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件不在回收站中");
        }

        try {
            // 如果是文件（非文件夹），删除OSS存储的文件
            if (file.getIsFolder() == 0 && StringUtils.isNotBlank(file.getOssKey())) {
                try {
                    ossUtil.deleteFile(file.getOssKey());
                    logger.info("删除OSS文件成功：{}", file.getOssKey());
                } catch (Exception e) {
                    logger.warn("删除OSS文件失败：{}", file.getOssKey(), e);
                    // OSS删除失败不阻断数据库删除
                }
            }

            // 从数据库中彻底删除
            fileRepository.delete(file);

            // 更新用户存储使用量
            Long usedStorage = fileRepository.sumFileSizeByUserId(userId);
            User user = userRepository.findById(userId).orElseThrow();
            user.setUsedStorage(usedStorage);
            userRepository.save(user);

            logger.info("彻底删除文件成功：用户ID={}, 文件ID={}, 文件名={}", userId, fileId, file.getFileName());
        } catch (Exception e) {
            logger.error("彻底删除文件失败：用户ID={}, 文件ID={}", userId, fileId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "彻底删除文件失败");
        }
    }

    @Override
    public void clearRecycleBin(Long userId) {
        // 参数校验
        if (userId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        try {
            // 获取回收站中的所有文件
            List<File> recycleFiles = fileRepository.findByParentIdAndUserIdAndStatus(0L, userId, Constants.FileStatus.RECYCLE);

            for (File file : recycleFiles) {
                // 如果是文件（非文件夹），删除OSS存储的文件
                if (file.getIsFolder() == 0 && StringUtils.isNotBlank(file.getOssKey())) {
                    try {
                        ossUtil.deleteFile(file.getOssKey());
                        logger.info("删除OSS文件成功：{}", file.getOssKey());
                    } catch (Exception e) {
                        logger.warn("删除OSS文件失败：{}", file.getOssKey(), e);
                        // OSS删除失败不阻断数据库删除
                    }
                }
            }

            // 批量删除数据库记录
            fileRepository.deleteAll(recycleFiles);

            // 更新用户存储使用量
            Long usedStorage = fileRepository.sumFileSizeByUserId(userId);
            User user = userRepository.findById(userId).orElseThrow();
            user.setUsedStorage(usedStorage);
            userRepository.save(user);

            logger.info("清空回收站成功：用户ID={}, 删除文件数={}", userId, recycleFiles.size());
        } catch (Exception e) {
            logger.error("清空回收站失败：用户ID={}", userId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "清空回收站失败");
        }
    }

    @Override
    public Map<String, Object> generateDownloadUrl(Long userId, Long fileId) {
        // 参数校验
        if (userId == null || fileId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        // 检查文件状态
        if (!Integer.valueOf(Constants.FileStatus.NORMAL).equals(file.getStatus())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件不可用");
        }

        // 检查是否为文件夹
        if (file.getIsFolder() == 1) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件夹不支持下载");
        }

        // 检查OSS键是否存在
        if (StringUtils.isBlank(file.getOssKey())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件存储信息不完整");
        }

        try {
            // 生成临时下载链接（有效期1小时）
            String downloadUrl = ossUtil.generateDownloadUrl(file.getOssKey(), file.getFileName(), 3600);

            Map<String, Object> result = new HashMap<>();
            result.put("downloadUrl", downloadUrl);
            result.put("fileName", file.getFileName());
            result.put("fileSize", file.getFileSize());
            result.put("expireTime", System.currentTimeMillis() + 3600 * 1000); // 1小时后过期

            logger.info("生成下载链接成功：用户ID={}, 文件ID={}, 文件名={}", userId, fileId, file.getFileName());
            return result;
        } catch (Exception e) {
            logger.error("生成下载链接失败：用户ID={}, 文件ID={}", userId, fileId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "生成下载链接失败");
        }
    }

    @Override
    public Map<String, Object> generatePreviewUrl(Long userId, Long fileId) {
        // 参数校验
        if (userId == null || fileId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }

        // 查找文件
        File file = fileRepository.findByIdAndUserId(fileId, userId)
                .orElseThrow(() -> new BusinessException(ResultCode.ERROR.getCode(), "文件不存在或无权限"));

        // 检查文件状态
        if (!Integer.valueOf(Constants.FileStatus.NORMAL).equals(file.getStatus())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件不可用");
        }

        // 检查是否为文件夹
        if (file.getIsFolder() == 1) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件夹不支持预览");
        }

        // 检查OSS键是否存在
        if (StringUtils.isBlank(file.getOssKey())) {
            throw new BusinessException(ResultCode.ERROR.getCode(), "文件存储信息不完整");
        }

        // 检查文件类型是否支持预览
        String fileType = file.getFileType();
        boolean supportPreview = isPreviewSupported(fileType);

        try {
            // 生成临时预览链接（有效期30分钟）
            String previewUrl = ossUtil.generatePreviewUrl(file.getOssKey(), 1800);

            Map<String, Object> result = new HashMap<>();
            result.put("previewUrl", previewUrl);
            result.put("fileName", file.getFileName());
            result.put("fileType", file.getFileType());
            result.put("fileSize", file.getFileSize());
            result.put("supportPreview", supportPreview);
            result.put("expireTime", System.currentTimeMillis() + 1800 * 1000); // 30分钟后过期

            logger.info("生成预览链接成功：用户ID={}, 文件ID={}, 文件名={}", userId, fileId, file.getFileName());
            return result;
        } catch (Exception e) {
            logger.error("生成预览链接失败：用户ID={}, 文件ID={}", userId, fileId, e);
            throw new BusinessException(ResultCode.ERROR.getCode(), "生成预览链接失败");
        }
    }

    /**
     * 检查文件类型是否支持预览
     */
    private boolean isPreviewSupported(String fileType) {
        if (StringUtils.isBlank(fileType)) {
            return false;
        }

        // 支持预览的文件类型
        String[] supportedTypes = {
            "image/jpeg", "image/jpg", "image/png", "image/gif", "image/bmp", "image/webp",
            "text/plain", "text/html", "text/css", "text/javascript",
            "application/pdf",
            "video/mp4", "video/avi", "video/mov", "video/wmv",
            "audio/mp3", "audio/wav", "audio/ogg"
        };

        for (String supportedType : supportedTypes) {
            if (supportedType.equalsIgnoreCase(fileType)) {
                return true;
            }
        }

        return false;
    }
}